import unittest
from main.cpu import CPU
from main.process import PCB ,  Instruction , Process , ProcessStates
from main.dataStructures import ProcessesQueue
from main.ioDevs import IO
from main.kernel import Kernel
from main.memory import MMU_ContinuousAssignment,BestAdjustment,PhysicalMemory,Disk

class TestCPU(unittest.TestCase):
    def setUp(self):
        self.memory = PhysicalMemory(90,BestAdjustment())
        self.mmu = MMU_ContinuousAssignment()
        self.io =  self.io = IO()
        self.disk = Disk()
        #
        self.pcb1 =  PCB(3)
        self.instruction1 =  Instruction(2,5)
        self.instruction2 =  Instruction(2,5)
        self.instruction3 =  Instruction(2,3)
        self.instruction4 =  Instruction(2,4)
        self.process1 = Process(self.pcb1)#cpu
        self.process1.addInstruction(self.instruction1)
        self.process1.addInstruction(self.instruction2)
        self.process1.addInstruction(self.instruction3)
        self.process1.addInstruction(self.instruction4)
        #
        self.pcb2 =  PCB(4)
        self.process2 = Process(self.pcb2)#cpu
        self.process2.addInstruction(self.instruction1)
        #
        self.pcb3 =  PCB(5)
        self.instruction5 =  Instruction(1,3)
        self.instruction6 =  Instruction(1,4)
        self.process3 = Process(self.pcb3)#io
        self.process3.addInstruction(self.instruction5)
        self.process3.addInstruction(self.instruction6)
        #
        self.process4 = Process(PCB(6))
        self.instruction7 =  Instruction(1,3)
        self.instruction8 =  Instruction(1,4)
        self.process4.addInstruction(self.instruction7)
        self.process4.addInstruction(self.instruction8)
        #
        self.procesos = ProcessesQueue()
        self.finishedProcess =ProcessesQueue()
        self.cpu = CPU(self.procesos,self.io,self.memory,self.disk)
        self.aStatus = ProcessStates.READY
        self.aStatus2 = ProcessStates.FINISHED
        #
        self.disk.addProcessToDisk(self.process1)
        self.disk.addProcessToDisk(self.process2)
        self.disk.addProcessToDisk(self.process3)
        self.disk.addProcessToDisk(self.process4)
        self.kernel = Kernel(None,None,self.cpu,None,None,ProcessesQueue(),self.memory,self.disk)
    
    
    
    #Testea un proceso de Cpu con mas de una instruccion 
    def test_whenACPUexecuteAprocessWithMoreThan1Instruction(self):
        self.memory.processesTable.append(self.process1)
        self.memory.cells[0] = self.instruction1
        self.memory.cells[1] = self.instruction2
        self.memory.cells[2] = self.instruction3
        self.memory.cells[3] = self.instruction4
        self.pcb1.baseRegister = 0
        self.cpu.currentProcess = self.process1
        self.cpu.addKernelToCpu(self.kernel)
        self.cpu.executeProcess()                                                  #Ejecuta un proceso con mas de una instruccion
        self.assertEquals(self.process1.pcb.status,ProcessStates.READY)            #El estado del proceso deberia ser Ready
        self.assertEquals(self.process1.pcb.currentInstruction,self.instruction2)  #Su instruccion actual ahora es self.instruction2, ya ejecuto self.instruction1
        self.assertEquals(self.cpu.currentProcess, None )                          #El currentProcess del Cpu deberia ser None, porque envio al proceso a la cola de listos
        self.assertEquals(self.cpu.readyProcessesQueue.popTheLast(),self.process1) #El proceso recien ejecutada su primera instruccion, se agrega al final de la readyQueueProcess
       
       
       
    #Testea un proceso de Cpu con una instruccion        
    def test_whenACPUexecuteAprocessWith1Instruction(self):
        self.memory.processesTable.append(self.process2)
        self.memory.cells[0] = self.instruction1
        self.pcb2.baseRegister = 0
        self.cpu.currentProcess = self.process2
        self.cpu.addKernelToCpu(self.kernel)
        self.cpu.executeProcess()                                                  #Ejecuta un proceso con una instruccion
        self.assertEquals(self.process2.pcb.status, ProcessStates.FINISHED)        #El estado del proceso deberia ser Finished
        self.assertEquals(self.process2.pcb.currentInstruction, None )             #Su instruccion actual ahora es None debido a que ya ejecuto su ultima instruccion
        self.assertEquals(self.cpu.currentProcess, None )                          #El currentProcess del Cpu deberia ser None, porque envio el proceso a la cola de terminados
        self.assertEquals(self.kernel.finishedProcessesQueue.popTheLast(),self.process2)#El proceso recien ejecutada su ultima instruccion, se agrega al final de la finishedQueueProcess
        
        
    #Testea un proceso de IO    
    def test_whenACPUhasToSendAprocessToTheIOQueue(self):
        self.memory.processesTable.append(self.process3)
        self.memory.cells[0] = self.instruction5
        self.memory.cells[1] = self.instruction6
        self.pcb3.baseRegister = 0
        self.cpu.currentProcess = self.process3
        self.cpu.addKernelToCpu(self.kernel)
        self.cpu.executeProcess()                                                   #Ejecuta un proceso con una instruccion de IO
        self.assertEquals(self.cpu.io.processesWaitingForIO.popTheLast() ,self.process3)#El proceso es enviado a la cola de procesos que esperan usar algun device IO
        
    def test_whenAProcessIsntInMemory(self):
        self.cpu.currentProcess = self.process4
        self.cpu.addKernelToCpu(self.kernel)
        self.cpu.executeProcess()
        self.assertEquals(self.memory.cells[0] , self.instruction7)
        self.assertEquals(self.memory.cells[1] , self.instruction8)
            
                        
suite = unittest.TestLoader().loadTestsFromTestCase(TestCPU)
unittest.TextTestRunner(verbosity=2).run(suite)